import {Mesh,Matrix4,Vector3,Vector2,Color,Quaternion,BufferGeometry,
		Group,InstancedMesh,Raycaster,Object3D,SphereGeometry,MeshBasicMaterial
} from './threejs/three.module.js';
import { TransformControls } from './threejs/jsm/controls/TransformControls.js';

function Vector3Array(vector3Array) {
	this.vector3Array = vector3Array || [];

	this.max = new Vector3();
	this.min = new Vector3();

	this._getMaxMin();
};
Object.assign(Vector3Array.prototype, {
	_getMaxMin: function () {
		if (this.vector3Array.length > 0) {
			this.max = this.vector3Array[0].clone();
			this.min = this.vector3Array[0].clone();

			for (var i = 1; i < this.vector3Array.length; i++) {
				if (this.max.x < this.vector3Array[i].x) {
					this.max.x = this.vector3Array[i].x;
				}
				if (this.max.y < this.vector3Array[i].y) {
					this.max.y = this.vector3Array[i].y;
				}
				if (this.max.z < this.vector3Array[i].z) {
					this.max.z = this.vector3Array[i].z;
				}

				if (this.min.x > this.vector3Array[i].x) {
					this.min.x = this.vector3Array[i].x;
				}
				if (this.min.y > this.vector3Array[i].y) {
					this.min.y = this.vector3Array[i].y;
				}
				if (this.min.z > this.vector3Array[i].z) {
					this.min.z = this.vector3Array[i].z;
				}
			}
		}
	},

	getCenter: function () {
		var center = new Vector3();
		return center.addVectors(this.max, this.min).multiplyScalar(0.5);
	}
});

var PModelEditor = function (renderer,scene,camera,ctrlType,controls,renderfun,parentPtr) {
	this.render = renderer;
	this.scene = scene;
	this.sceneObjects = scene.getObjectByName('sceneObjects');
	this.camera = camera;
	this.controls = controls;
	this.ctrlType = ctrlType;
	this.renderfun = this._bind(parentPtr, renderfun);
	this.mesh = null;
	this.operationType = '';

	this.MOUSEDOWN = "mouseDown";
	this.MOUSEMOVE = "mouseMove";
	this.MOUSEDOWNMOVE = "mouseDownMove"; // 按下鼠标并移动
	this.MOUSEDOWNNOMOVE = "mouseDownNoMove"; // 按下鼠标没有移动
	this.MOUSENO = "mouseNo";
	this.mouseState = this.MOUSENO; // 记录鼠标的状态
	this.mouseX = 0; // 鼠标的位置
	this.mouseY = 0;

	this.mouse = new Vector2();
	this.pickupPosition = new Vector3();
	this.raycaster = new Raycaster();
	var domElement=this.render.domElement;
	this.domElement = (domElement !== undefined) ? domElement : document;
	this.transformCtrls = null;

	this.onCtrlDragchange = this._bind(this, this._onCtrlDragchange);
	this.onDocumentKeydown = this._bind(this, this._onDocumentKeydown);
	this.onMouseDown = this._bind(this, this._onMouseDown);
	this.onMouseMove = this._bind(this, this._onMouseMove);
	this.onMouseUp = this._bind(this, this._onMouseUp);
	this.init();
};

PModelEditor.prototype = {
	constructor: PModelEditor,

	init: function() {
		var _this = this;
		if(this.ctrlType === 'Orbit') {
			this.controls.update();
			this.controls.addEventListener( 'change', this.renderfun );
		}

		var tcontrol = new TransformControls( this.camera, this.render.domElement );
		if(this.ctrlType === 'Orbit') {
			tcontrol.addEventListener( 'change', this.renderfun );
		}
		// tcontrol.setMode('rotate');

		tcontrol.addEventListener( 'dragging-changed', this.onCtrlDragchange );
		const gizmo = tcontrol.getHelper();
		this.scene.add( gizmo );
		// this.scene.add( tcontrol );

		window.addEventListener( 'keydown', this.onDocumentKeydown );
		this.transformCtrls = tcontrol;

		this.domElement.addEventListener( 'mousemove', this.onMouseMove, false );
		this.domElement.addEventListener( 'mousedown', this.onMouseDown, false );
		this.domElement.addEventListener( 'mouseup', this.onMouseUp, false );

		this.moveobjs = [];
		const geometry = new SphereGeometry( 3, 32, 16 );
		const material = new MeshBasicMaterial( { color: 0xffff00 } );
		for(let i=0;i<16;i++) {
			this.moveobjs.push( new Mesh( geometry, material ) );
		}
	},

	dispose: function() {
		var _this = this;
		if (this.transformCtrls) {
			if(this.ctrlType === 'Orbit') {
				this.controls.removeEventListener( 'change', this.renderfun );
				this.transformCtrls.removeEventListener( 'change', this.renderfun );
			}
			this.transformCtrls.removeEventListener( 'dragging-changed', this.onCtrlDragchange );
			window.removeEventListener( 'keydown', this.onDocumentKeydown );

			if(null!=this.mesh) this.transformCtrls.detach();
			this.mesh = null;
			this.transformCtrls.dispose();
			this.scene.add( this.transformCtrls );
			this.transformCtrls = null;
		}

		this.domElement.removeEventListener( 'mousemove', this.onMouseMove, false );
		this.domElement.removeEventListener( 'mousedown', this.onMouseDown, false );
		this.domElement.removeEventListener( 'mouseup', this.onMouseUp, false );

		var Idiv = document.getElementById('editor_model');
		if(Idiv) Idiv.style.display = "none";
	},

	ctrlControl: function ( ctrlType,controls ) {
		this.dispose();
		this.controls = controls;
		this.ctrlType = ctrlType;
		this.init();
	},

	_onMouseDown: function (event) {
		event.preventDefault();

		this.mouseState = this.MOUSEDOWN;
		this.mouseX = event.clientX;
		this.mouseY = event.clientY;
	},
	_onMouseMove: function (event) {
		event.preventDefault();

		// 设置鼠标状态
		if ((this.mouseState === this.MOUSEDOWN) || (this.mouseState === this.MOUSEDOWNMOVE)) {
			if (this.mouseX === event.clientX && this.mouseY === event.clientY) {
				this.mouseState = this.MOUSEDOWNNOMOVE;
			} else {
				this.mouseState = this.MOUSEDOWNMOVE;
			}
		} else {
			this.mouseState = this.MOUSEMOVE;
		}
		this.ctrlObjectMove();
	},
	_onMouseUp: function (event) {
		event.preventDefault();

		if (this.mouseState === this.MOUSEDOWNNOMOVE || this.mouseState === this.MOUSEDOWN) {
			this.mouseClick(event);
		}
		this.mouseState = this.MOUSENO;

		if(null!=this.mesh && this.mesh.name == 'editSphereModel' ) {
			let lobj = this.sceneObjects.getObjectByProperty('uuid', this.mesh.userData.parentid);
			if(lobj && !this.transformCtrls.dragging) {
				lobj.geometry.computeBoundingSphere();
			}
		}
	},

	mouseClick: function ( event ) {
		var _this = this;

		if(!_this.controls.enabled) return;
		_this._convertCoordinate(event);
		_this.raycaster.setFromCamera( _this.mouse, _this.camera );

		var intersects = _this.raycaster.intersectObjects( _this.sceneObjects.children, true );
		if(intersects.length>0) {
			let intersect = null;
			for(let i=0;i<intersects.length;i++) {
				intersect = intersects[i];
				if('Mesh'==intersect.object.type) {
					break;
				}
			}
			if(intersect) {
				_this.pickupPosition.set(intersect.point.x,intersect.point.y,intersect.point.z);
				_this.attachMesh(intersect.object);
			}
		} else {
			this.detachMesh();
			this.ctrlSphereModels();
		}

	},

	_onCtrlDragchange: function ( event ) {
		this.controls.enabled = ! event.value;
	},

	_onDocumentKeydown: function ( event ) {
		switch ( event.keyCode ) {

			case 84: // T
				this.transformCtrls.setMode( "translate" );
				this.transformCtrls.showX = true;
				this.transformCtrls.showY = true;
				this.transformCtrls.showZ = true;
				break;
			case 82: // R
				this.transformCtrls.setMode( "rotate" );
				// this.transformCtrls.showX = false;
				// this.transformCtrls.showZ = false;
				break;

		}
	},

	_bind: function (scope, fn) {
		return function () {
			fn.apply(scope, arguments);
		};
	},

	detachMesh: function() {
		if(null!=this.mesh) {
			this.transformCtrls.detach();
			this.mesh.userData.editModel = false;
			this.mesh = null;
		}
	},

	ctrlSphereModels: function(bCtrl) {
		let moItem;
		for(let i=0;i<this.moveobjs.length;i++) {
			moItem = this.moveobjs[i];
			moItem.position.y +=10000000;
			moItem.userData.parentid = '';
		}
	},

	attachMesh: function(mesh) {
		let bSameMesh = false;
		this.ctrlMeshObject( mesh );
		if(null!=this.mesh && null!=mesh) {
			if( this.mesh.name == 'editSphereModel') {
				if(this.mesh.userData.parentid!=mesh.uuid)  bSameMesh = false;
				else bSameMesh = true;
			} else if(this.mesh.uuid==mesh.uuid) {
				bSameMesh = true;
			}

			if(!bSameMesh) {
				this.detachMesh();

				if( mesh.name != 'editSphereModel' && (this.moveobjs[0].userData.parentid!=mesh.uuid) ) {
					this.ctrlSphereModels();
				}
			}
		}

		// this.sceneObjects.attach(objVal);
		this.transformCtrls.attach( mesh );
		this.mesh = mesh;
	},

	ctrlMeshObject2: function(mesh,extFaceIndex) {
		if(null!=mesh && mesh.name != 'editSphereModel' && !mesh.userData.editModel) {
			let vecList=[],vecCenter,vecPos,gItem;
			let icount=0,objVal;

			function createVec(index) {
				vecPos = new Vector3( mesh.geometry.attributes.position.array[ (index)*3 ],
					mesh.geometry.attributes.position.array[ ((index)*3)+1 ],
					mesh.geometry.attributes.position.array[ ((index)*3)+2 ] );
				vecList.push( vecPos );
			}

			for(let i=0;i<mesh.geometry.index.array.length;i+=6) {
				icount=i/6;
				if(icount!=extFaceIndex) {
					vecList=[];
					gItem = mesh.geometry.index.array[i];
					createVec(gItem);
					gItem = mesh.geometry.index.array[i+1];
					createVec(gItem)
					gItem = mesh.geometry.index.array[i+2];
					createVec(gItem)
					gItem = mesh.geometry.index.array[i+3];
					createVec(gItem)
					gItem = mesh.geometry.index.array[i+4];
					createVec(gItem)
					gItem = mesh.geometry.index.array[i+5];
					createVec(gItem)

					var vector3Array = new Vector3Array(vecList);
					vecCenter = vector3Array.getCenter();
					// console.log(vecList,vecCenter);
					objVal = this.moveobjs[ icount ];
					objVal.name = 'editSphereModel'
					objVal.position.copy(vecCenter);
					objVal.userData.position.copy(vecCenter);
				}
			}
		}
	},

	ctrlMeshObject: function(mesh) {
		if(null!=mesh && mesh.name != 'editSphereModel' && !mesh.userData.editModel) {
			let vecList=[],vecCenter,vecPos,gItem;
			let icount=0,objVal;

			function createVec(index) {
				vecPos = new Vector3( mesh.geometry.attributes.position.array[ (index)*3 ],
					mesh.geometry.attributes.position.array[ ((index)*3)+1 ],
					mesh.geometry.attributes.position.array[ ((index)*3)+2 ] );
				vecList.push( vecPos );
			}

			for(let i=0;i<mesh.geometry.index.array.length;i+=6) {
				icount=i/6;
				vecList=[];
				gItem = mesh.geometry.index.array[i];
				createVec(gItem);
				gItem = mesh.geometry.index.array[i+1];
				createVec(gItem)
				gItem = mesh.geometry.index.array[i+2];
				createVec(gItem)
				gItem = mesh.geometry.index.array[i+3];
				createVec(gItem)
				gItem = mesh.geometry.index.array[i+4];
				createVec(gItem)
				gItem = mesh.geometry.index.array[i+5];
				createVec(gItem)

				var vector3Array = new Vector3Array(vecList);
				vecCenter = vector3Array.getCenter();
				// console.log(vecList,vecCenter);
				objVal = this.moveobjs[ icount ];
				objVal.name = 'editSphereModel'
				objVal.position.copy(vecCenter);
				objVal.userData = {
					face:icount,
					parentid:mesh.uuid,
					position:vecCenter.clone()
				};
				mesh.add(objVal);
				// this.sceneObjects.attach(objVal);
				// icount++;
			}
			mesh.userData.editModel = true;
			// for(let i=0;i<mesh.geometry.groups.length;i++) {//
			// 	gItem = mesh.geometry.groups[i];
			// 	vecList=[];
			// 	for(let j=0;j<gItem.count;j++) {
			// 		vecPos = new Vector3( mesh.geometry.attributes.position.array[ (gItem.start+j)*3 ],
			// 			mesh.geometry.attributes.position.array[ ((gItem.start+j)*3)+1 ],
			// 			mesh.geometry.attributes.position.array[ ((gItem.start+j)*3)+2 ] );
			// 		vecList.push( vecPos );
			//
			// 		console.log(vecPos);
			// 		this.moveobjs[i+j].position.copy(vecPos);
			// 		mesh.add(this.moveobjs[i+j]);
			// 	}
			// 	// var vector3Array = new Vector3Array(vecList);
			// 	// vecCenter = vector3Array.getCenter();
			// 	// console.log(vecList,vecCenter);
			// 	// this.moveobjs[i].position.copy(vecCenter);
			// 	// mesh.add(this.moveobjs[i]);
			// }
		}
	},

	ctrlObjectMove: function() {
		if(null!=this.mesh && this.mesh.name == 'editSphereModel' ) {
			let _this = this,sameVList;
			function movePos(index) {
				index *=3;
				lobj.geometry.attributes.position.array[index] +=posOffset.x;
				lobj.geometry.attributes.position.array[index+1] +=posOffset.y;
				lobj.geometry.attributes.position.array[index+2] +=posOffset.z;
			}
			let lobj = this.sceneObjects.getObjectByProperty('uuid',this.mesh.userData.parentid);
			let posOffset = {
				x:this.mesh.position.x - this.mesh.userData.position.x,
				y:this.mesh.position.y - this.mesh.userData.position.y,
				z:this.mesh.position.z - this.mesh.userData.position.z
			};
			if(lobj) {
				if(0!=posOffset.x || 0!=posOffset.y || 0!=posOffset.z) {
					let vecIndex = this.mesh.userData.face*6;

					sameVList = lobj.userData.facesamevec[this.mesh.userData.face];
					for(let i=0;i<sameVList.length;i++) movePos( sameVList[i] );
					movePos(lobj.geometry.index.array[ vecIndex ]);
					movePos(lobj.geometry.index.array[ vecIndex+1 ]);
					movePos(lobj.geometry.index.array[ vecIndex+2 ]);
					movePos(lobj.geometry.index.array[ vecIndex+5 ]);
					lobj.geometry.attributes.position.needsUpdate = true;
					this.mesh.userData.position.copy(this.mesh.position);

					_this.ctrlMeshObject2(lobj,this.mesh.userData.face);
				}

			}
		}
	},

	_convertCoordinate: function (event) {
		var left = this._getOffsetLeft(this.domElement);
		var top = this._getOffsetTop(this.domElement);
		if( null!=event.touches ) {
			if(event.touches>0) {
				var touch = event.touches[0];
				this.mouse.x = ((touch.pageX - left) / this.domElement.offsetWidth) * 2 - 1;
				this.mouse.y = -((touch.pageY - top) / this.domElement.offsetHeight) * 2 + 1;
			} else {
				this.mouse.x = ((event.changedTouches[0].pageX - left) / this.domElement.offsetWidth) * 2 - 1;
				this.mouse.y = -((event.changedTouches[0].pageY - top) / this.domElement.offsetHeight) * 2 + 1;
			}
		} else {
			this.mouse.x = ((event.clientX - left) / this.domElement.offsetWidth) * 2 - 1;
			this.mouse.y = -((event.clientY - top) / this.domElement.offsetHeight) * 2 + 1;
		}

	},
	_getOffsetLeft: function (obj) {
		var l = obj.offsetLeft;
		while (obj = obj.offsetParent) {
			l += obj.offsetLeft;
		}
		return l;
	},
	_getOffsetTop: function (obj) {
		var t = obj.offsetTop;
		while (obj = obj.offsetParent) {
			t += obj.offsetTop;
		}
		return t;
	},
};

export { PModelEditor }
